//
//  NewConnections.m
//  SolarSeek
//
//  Created by Erick Fejta on 05/05/26.
//  Copyright 2005 __MyCompanyName__. All rights reserved.
//

#import "NewConnections.h"

#import "NetSocket.h"

#ifdef _SOLARSEEK_NEW_

NSTimeInterval DefaultTimeIntervalTimeout = 20.0;
unsigned DefaultSpeedUpCeiling = 0;
unsigned DefaultSpeedDownCeiling = 0;

@interface NewConnection (Private)
- (void)updateInformation;
@end


@implementation NewConnection

- (id)init {
	if(self = [super init]) {
		_connected = NO;
		_addressLocal = _addressRemote = nil;
		_portLocal = _portRemote = nil;
		_state = 0;
		
		_speedUpCeiling = _speedDownCeiling = NSNotFound;
		_speedUpCurrent = _speedDownCurrent = 0;
		
		_timeIntervalTimeout = -1.0;
		_activityLast = nil;
		
		_speedsDown = [[NSMutableArray alloc] initWithCapacity:20];
		_speedsUp = [[NSMutableArray alloc] initWithCapacity:20];
		_countTransferredUpLast = _countTransferredDownLast = 0;
	}
	return self;
}

- (void)dealloc {
	if([_socket delegate] == self)
		[_socket setDelegate:nil];
	[_socket release];
	_socket = nil;
	[_addressLocal release];
	[_addressRemote release];
	_state = 0;
	[_activityLast release];
	_activityLast = nil;
	[_speedsUp release];
	[_speedsDown release];
	_speedsUp = _speedsDown = nil;
	[super dealloc];
}

#pragma mark accessor methods (public)

- (BOOL)isConnected { return _connected; }
- (NSString *)addressLocal { return _addressLocal; }
- (NSString *)addressRemote { return _addressRemote; }
- (UInt16)portLocal { return _portLocal; }
- (UInt16)portRemote { return _portRemote; }
- (int)state { return _state; }
- (unsigned)countBytesAvailable { return [_socket incomingBufferLength]; }
- (unsigned)countBytesSending { return [_socket outgoingBufferLength]; }

- (unsigned)speedUpCeiling { return _speedUpCeiling==NSNotFound?DefaultSpeedUpCeiling:_speedUpCeiling; }
- (unsigned)speedDownCeiling { return _speedDownCeiling==NSNotFound?DefaultSpeedDownCeiling:_speedDownCeiling; }
- (unsigned)speedUpCurrent { return _speedUpCurrent; }
- (unsigned)speedDownCurrent { return _speedDownCurrent; }

- (NSTimeInterval)timeIntervalTimeout { return _timeIntervalTimeout==-1.0?DefaultTimeIntervalTimeout:_timeIntervalTimeout; }

- (void)setState:(int)state { _state = state; }
- (void)setSpeedUpCeiling:(unsigned)bytespersecond { _speedUpCeiling = bytespersecond; }
- (void)setSpeedDownCeiling:(unsigned)bps { _speedDownCeiling = bps; }

- (void)setTimeIntervalTimeout:(NSTimeInterval)timeout { _timeIntervalTimeout = timeout; }

#pragma mark accessor methods (private)

- (void)setIsConnected:(BOOL)conn { _connected = conn; }
- (void)setAddressLocal:(NSString *)addressLocal { _addressLocal = [addressLocal retain]; }
- (void)setAddressRemote:(NSString *)addressRemote { _addressRemote = [addressRemote retain]; }
- (void)setPortLocal:(UInt16)port { _portLocal = port; }
- (void)setPortRemote:(UInt16)port { _portRemote = port; }
- (void)setSpeedUpCurrent:(unsigned)current { _speedUpCurrent = current; }
- (void)setSpeedDownCurrent:(unsigned)current { _speedDownCurrent = current; }

#pragma mark actions

- (void)connectToSocket:(NetSocket *)socket {
	NSAssert2([self isConnected]==NO,@"Already connected.  Cannot connect to socket on %@:%d.",[socket remoteHost],[socket remotePort]);
	NSAssert([socket isConnected]==YES,@"Cannot connect to an unconnected socket.");
	_socket = [socket retain];
	[_socket setDelegate:self];
	[self updateInformation];
}

- (BOOL)connectToHost:(NSString *)address onPort:(UInt16)port { 
	NSAssert2([self isConnected]==NO,@"Already connected. Cannot connect to %@:%d.",address,port);
	
	_socket = [[NetSocket netsocketConnectedToHost:address port:port] retain];
	[_socket setDelegate:self];
	return _socket!=nil;
}

- (void)disconnect {
	if(_socket) {
		[_socket close];
		[_socket setDelegate:nil];
		_socket = nil;
		[self setIsConnected:NO];
	}
}


- (void)sendMessage:(Message *)message {
	NSAssert([self isConnected],@"Must be connected to send a message.");
	
	NSData *data = [message data];
	unsigned int length = [data length];
	length = NSSwapHostIntToLittle(length);
	NSMutableData *mdata = [NSMutableData dataWithCapacity:length+4];
	[mdata appendBytes:&length length:sizeof(length)];
	[mdata appendData:data];
	[self sendData:mdata];
}

- (Message *)readMessageData:(unsigned)countBytes {
	NSAssert1([self countBytesAvailable]>=countBytes,@"Must have at least %d bytes available to read a message.",countBytes);
	
	NSData *data = [self readData:countBytes];
	return [Message messageWithData:data];
}

- (unsigned)readMessageLength {
	NSAssert([self countBytesAvailable]>=4,@"Must have at least 4 bytes available to read a message.");
	
	unsigned int length = NSNotFound, test=NSNotFound;
	[[self readData:4] getBytes:&test];
	test = NSSwapLittleIntToHost(test);
	if(test==0) {
		NSLog(@"CONNECTION: %@:%d sent a zero byte message.  Disconnecting...",[self addressRemote],[self portRemote]);
		[self disconnect];
	}
	else if(test > (1024*1024)) {
		NSLog(@"CONNECTION: %@:%d sent an excessively long message (%d bytes).  Disconnecting...",[self addressRemote],[self portRemote],test);
		[self disconnect];
	}
	else	
		length = test;
	return length;
}
	
- (void)sendData:(NSData *)data {
	NSAssert([self isConnected],@"Must be connected to send data.");
	
	[_socket writeData:data];
}

- (NSData *)readData:(unsigned)countBytes {
	NSAssert1([self countBytesAvailable]>=countBytes,@"There are not %d bytes available.",countBytes);
	return [_socket readData:countBytes];
}


#pragma mark static methods

+ (void)setDefaultTimeIntervalTimeout:(NSTimeInterval)timeout { DefaultTimeIntervalTimeout = timeout; }
+ (void)setDefaultSpeedUpCeiling:(unsigned)bps { DefaultSpeedUpCeiling = bps; }
+ (void)setDefaultSpeedDownCeiling:(unsigned)bps { DefaultSpeedDownCeiling = bps; }

#pragma mark netsocket delegate

- (void)updateInformation {
	NetSocket *sock = _socket;
	[self setAddressLocal:[sock localHost]];
	[self setPortLocal:[sock localPort]];
	[self setAddressRemote:[sock remoteHost]];
	[self setPortRemote:[sock remotePort]];
	[self setSpeedUpCurrent:0];
	[self setSpeedDownCurrent:0];
	[self setIsConnected:[sock isConnected]];
}

- (void)netsocketConnected:(NetSocket*)inNetSocket {
	NSAssert(inNetSocket==_socket,@"Sockets do not match.");
	if([inNetSocket isConnected]) {
		[self updateInformation];
	}
	else {
		[self disconnect];
	}
}

- (void)netsocketConnectionTimedOut:(NetSocket*)inNetSocket {
	[self disconnect];
}

- (void)netsocketDisconnected:(NetSocket*)inNetSocket {
	[self disconnect];
}

- (void)netsocket:(NetSocket*)inNetSocket connectionAccepted:(NetSocket*)inNewNetSocket {
	[inNewNetSocket close];
	[inNewNetSocket release];
	[NSException raise:@"StrangeException" format:@"%@:%d should not be accepting connections.",[self addressLocal],[self portLocal]];
}

- (void)netsocketDataAvailable:(NetSocket*)inNetSocket {
	[self willChangeValueForKey:@"countBytesAvailable"];
	[self didChangeValueForKey:@"countBytesAvailable"];
}

- (void)netsocketDataSent:(NetSocket*)inNetSocket {
	[self willChangeValueForKey:@"countBytesSending"];
	[self didChangeValueForKey:@"countBytesSending"];
}


@end


@interface ConnectionHandle : NSObject {
	NewConnection *connection;
	int _local, _remote;
}

- (BOOL)isAnswerable;	//can we answer his calls.
- (BOOL)isCallable;		//can he answer our calls.

- (void)setLocallyFirewalled:(BOOL)firewalled;
- (void)setRemotelyFirewalled:(BOOL)firewalled;
- (NewConnection *)connection;
@end

@implementation ConnectionHandle
- (id)init {
	if(self = [super init]) {
		connection = [[NewConnection alloc] init];
		_local = _remote = 0;
	}
	return self;
}

- (void)dealloc {
	[connection release];
	connection = nil;
	[super dealloc];
}

- (BOOL)isAnswerable { _local != 2; }
- (BOOL)isCallable { _remote != 2; }

- (void)unsetLocal {
	_local = 0;
}
- (void)scheduleUnsetLocal {
	[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(unsetLocal) object:nil];
	[self performSelector:@selector(unsetLocal) withObject:nil afterDelay:60.0*60.0*2.0]; //2 hrs
}

- (void)setLocallyFirewalled:(BOOL)firewalled { 
	_local = 2|firewalled; 
	[self scheduleUnsetLocal];
}

- (void)unsetRemote {
	_remote = 0;
}
- (void)scheduleUnsetRemote {
	[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(unsetRemote) object:nil];
	[self performSelector:@selector(unsetRemote) withObject:nil afterDelay:60.0*60.0*2.0]; //2 hrs
}
- (void)setRemotelyFirewalled:(BOOL)firewalled { 
	_remote = 2|firewalled; 
	[self scheduleUnsetRemote];
}

@end

@implementation PeerConnectionHandle 
- (id)initWithPeer:(Peer *)p {
	NSAssert(p,@"Peer must be set.");
	if(self = [super init]) {
		peer = [p retain];
	}
	return self;
}

- (void)dealloc {
	[peer release];
	peer = nil;
	[super dealloc];
}

- (Peer *)peer { return peer; }
@end


@implementation TransferConnectionHandle
- (id)initWithTransfer:(Transfer *)t {
	NSAssert(t,@"Transfer must be set.");
	if(self = [super init]) {
		transfer = [t retain];
	}
	return self;
}

- (Transfer *)transfer { return transfer; }
@end

#endif